﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.ComponentModel;
using System.Xml;
using System.ComponentModel.Design;
using System.Reflection;
using System.Windows.Forms;

namespace UmcProvider
{
    public interface IUmc
    {
        //
        //string Name { get; }
    }
    public interface IControl : IUmc
    {
        bool Enabled { get; }
        //bool Visible { get; }
        Size Size { get; }
        Point Location { get; }
        string Text { get; }
        Color BackColor { get; }
        Font Font { get; }
        Color ForeColor { get; }
    }
    [AttributeUsage(AttributeTargets.Interface, Inherited = false, AllowMultiple = false), Serializable]
    public sealed class ExcludePropertyAttribute : Attribute
    { }

    public interface IToolStrip : IUmc
    {

    }
    //
    public interface IConverter
    {
        XmlNode[] Serialize(IComponent root, XmlDocument document);
        IComponent DeSerialize(IDesignerHost host, XmlDocument document, XmlNode[] node);
    }



    public abstract class DefaultConverter<C, T> : IConverter
        where C : IUmc
        where T : IComponent
    {
        protected static T instance = Activator.CreateInstance<T>();
        protected static String[] excludedPropArr = { "Bounds", "Left", "Top", "ImeMode", "Parent", "WindowTarget" };
        protected static IEnumerable<String> properties = GetUmcProperties();


        public virtual XmlNode[] Serialize(IComponent root, XmlDocument document)
        {
            if (root is Control)
            {
                Control ctl = root as Control;

                XmlNode node = document.CreateNode(XmlNodeType.Element, ctl.GetType().Name, null);

                // common properties
                foreach (var name in properties)
                {
                    object value = typeof(T).GetProperty(name).GetValue(root, null);
                    if (!EqualDefaultValue(name, value))
                    {
                        node.Attributes.Append(document.CreateAttribute(name))
                            .Value = GetUmcValue(value);
                    }
                }

                return new XmlNode[] { node };
            }

            throw new NotImplementedException();
        }


        public virtual IComponent DeSerialize(IDesignerHost host, XmlDocument document, XmlNode[] node)
        {
            IComponent component = host.CreateComponent(typeof(T));
            //T component = Activator.CreateInstance<T>();

            if (component is Control)
            {
                foreach (XmlAttribute attr in node.First().Attributes)
                {
                    SetProperty(component, attr);
                }

                return component;
            }

            throw new NotImplementedException();
        }

        private void SetProperty(IComponent component, XmlAttribute attr)
        {
            var prop = typeof(T).GetProperty(attr.Name);
            if (prop != null)
            {
                prop.SetValue(component, GetUmcValue(attr.Name, attr.Value, prop.PropertyType), null);
            }
        }

        private object GetUmcValue(string name, string value, Type type)
        {
            if (type == typeof(Size))
            {
                var source = value.Split(',');
                return new Size(int.Parse(source[0]), int.Parse(source[1]));
            }
            else if (type == typeof(Point))
            {
                var source = value.Split(',');
                return new Point(int.Parse(source[0]), int.Parse(source[1]));
            }
            else if (type == typeof(Font))
            {
                var source = value.Split(',');
                return new Font(source[0], float.Parse(source[1]));
            }
            else if (type == typeof(Color))
            {
                var source = value.Split(',');
                return Color.FromArgb(int.Parse(source[0]), int.Parse(source[1]), int.Parse(source[2]));
            }
            else if (type == typeof(Boolean))
            {
                return bool.Parse(value);
            }
            else if (type == typeof(Int32))
            {
                return int.Parse(value);
            }
            else if (type == typeof(Char))
            {
                return value[0];
            }
            else if (type == typeof(ScrollBars))
            {
                return Enum.Parse(typeof(ScrollBars), value);
            }
            else if (type == typeof(View))
            {
                return Enum.Parse(typeof(View), value);
            }
            else if (type == typeof(ImageList))
            {
                //TODO
                return null;
            }
            else
                return value;
        }

        private static IEnumerable<String> GetIncludeUmcProperties()
        {
            List<String> props = new List<String>();

            typeof(C).GetInterfaces().All(p =>
            {
                props.AddRange(p.GetProperties().Select(q => q.Name));
                return true;
            });
            return props;
        }

        private static IEnumerable<String> GetExceptUmcProperties()
        {
            List<Type> allTypes = new List<Type>();
            List<Type> allInterfaces = new List<Type>();
            List<String> allProps = new List<String>();
            List<String> excludeProps = new List<String>();
            BindingFlags propFilter = BindingFlags.Public | BindingFlags.Instance;

            for (Type curType = typeof(T); null != curType; curType = curType.BaseType)
            {
                allTypes.Add(curType);
                allInterfaces.AddRange(curType.GetInterfaces());
            }

            // Get all these properties
            foreach (Type t in allTypes)
            {
                allProps.AddRange(
                    t.GetProperties(propFilter).
                      Where(p => (p.CanRead && p.CanWrite && p.IsDefined(typeof(CategoryAttribute), false))).
                      Select(p => p.Name));
            }

            excludeProps.AddRange(excludedPropArr);
            // Get all these excluded properties
            foreach (Type t in allInterfaces.Where(i => i.IsDefined(typeof(ExcludePropertyAttribute), false)).Distinct())
                excludeProps.AddRange(t.GetProperties().Select(p => p.Name));

            return allProps.Distinct().Except(excludeProps.Distinct()).ToArray();
        }

        // Helper
        private static IEnumerable<String> GetUmcProperties()
        {
            Type firstExcludeInterface = null;
            IEnumerable<String> props = null;

            firstExcludeInterface = typeof(C).GetInterfaces().
                FirstOrDefault(t => t.IsDefined(typeof(ExcludePropertyAttribute), false));

            if (null != firstExcludeInterface)
            {
                props = GetExceptUmcProperties();
            }
            else
            {
                props = GetIncludeUmcProperties();
            }

            return props.ToArray();
        }

        private bool EqualDefaultValue(string name, object value)
        {
            object defaultValue = typeof(T).GetProperty(name).GetValue(instance, null);

            if (defaultValue == null && value == null)
                return true;
            else if (defaultValue == null)
                return false;
            else if (value == null)
                return false;
            else if (defaultValue.ToString() == value.ToString())
                return true;
            else
                return false;
        }

        private string GetUmcValue(object v)
        {
            //TODO:

            if (v is Size)
                return string.Format("{0},{1}", ((Size)v).Width, ((Size)v).Height);
            else if (v is Point)
                return string.Format("{0},{1}", ((Point)v).X, ((Point)v).Y);
            else if (v is Font)
                return string.Format("{0}, {1}", ((Font)v).Name, ((Font)v).Size);
            else if (v is Color)
                return string.Format("{0},{1},{2}", ((Color)v).R, ((Color)v).G, ((Color)v).B);
            else if (v is ImageList)
                return string.Format("Local:{0}", ((ImageList)v).Tag);
            else
                return v.ToString();
        }
    }

    //
    public sealed class UmcSerializeFactory
    {
        private static Dictionary<Type, IConverter> map = new Dictionary<Type, IConverter>();

        public static IConverter Create(IComponent root)
        {
            Type t = root.GetType();

            if (map.ContainsKey(t))
            {
                return map[t];
            }
            else
            {
                Type umc;

                if (t.Name == "Body")
                {
                    umc = typeof(IUmc).Assembly.GetType(typeof(IUmc).Namespace + ".Umc" + t.Name);
                }
                else
                {
                    Assembly assm = Assembly.LoadFile(Environment.CurrentDirectory + @"\ChinaTelecom.SmartPhone.Controls.dll");
                    umc = assm.GetType("ChinaTelecom.SmartPhone.Controls.Umc" + t.Name);
                }
                if (umc != null)
                {
                    IConverter ict = Activator.CreateInstance(umc) as IConverter;
                    map.Add(t, ict);
                    return ict;
                }
                else
                    return null;
            }
        }
    }

    public sealed class UmcDeSerializeFactory
    {
        private static Dictionary<String, IConverter> map = new Dictionary<String, IConverter>();

        public static IConverter Create(string name)
        {
            if (map.ContainsKey(name))
            {
                return map[name];
            }
            else
            {
                Type umc;
                if (name == "Body")
                {
                    umc = typeof(IUmc).Assembly.GetType(typeof(IUmc).Namespace + ".Umc" + name);
                }
                else
                {
                    Assembly assm = Assembly.LoadFile(Environment.CurrentDirectory + @"\ChinaTelecom.SmartPhone.Controls.dll");
                    umc = assm.GetType("ChinaTelecom.SmartPhone.Controls.Umc" + name);
                }
                if (umc != null)
                {
                    IConverter ict = Activator.CreateInstance(umc) as IConverter;
                    map.Add(name, ict);
                    return ict;
                }
                else
                    return null;
            }
        }

        private static string GetUmcTypeName(string name)
        {
            if (name == "MenuItem")
                return typeof(IUmc).Namespace + ".Umc" + typeof(MenuStrip).Name;
            else
                return typeof(IUmc).Namespace + ".Umc" + name;
        }
    }
}
